package com.weareint.feigncopy;

import com.weareint.common.Constants;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 *
 *
 * <pre>
 *  实现 CacheMappingFeignDataService 接口的 抽象
 *  提供 cacheKey ，cacheKeyToIds，context() 基础方法
 * </pre>
 *
 * @author johnny
 * @date 2021-08-23 9:50 上午
 */
@Slf4j
public abstract class AbsCacheMappingFeignDataService<S, D, T>
        implements CacheMappingFeignDataService<S, D, T> {

    public CacheMappingContext context;

    public static String appNamePrefix;

    /**
     * 放入缓存的操作
     *
     * @param <D>
     */
    interface PutCacheOperator<D> {
        /**
         * 放入缓存的操作
         *
         * @param key : 缓存key
         * @param d : d
         */
        void putCache(String key, D d);
    }

    List<D> cacheFeignData(List<String> expireKeys, PutCacheOperator<D> putCacheOperator) {
        if (CollectionUtils.isNotEmpty(expireKeys)) {
            List<T> ids = cacheKeyToIds(expireKeys);
            List<D> ds = feignData(ids);
            if (log.isDebugEnabled()) {
                log.debug("【feign mapping expireIds：{} feignData : {}】", ids, ds);
            }
            List<FeignMapping> annotations = getCurrentFeignMapping();
            if (CollectionUtils.isEmpty(annotations)) {
                return ds;
            }
            FeignMapping annotation = annotations.get(0);
            // 先设置到缓存中
            ds.forEach(
                    d -> {
                        if (annotation != null
                                && annotation.beRelationClass().equals(context().dClass())) {
                            String beRelationId = annotation.beRelationId();
                            boolean flag = annotation.beRelationIdIsSuper();
                            try {
                                Field beRelationField;
                                if (flag) {
                                    beRelationField =
                                            d.getClass()
                                                    .getSuperclass()
                                                    .getDeclaredField(beRelationId);
                                } else {
                                    beRelationField = d.getClass().getDeclaredField(beRelationId);
                                }
                                beRelationField.setAccessible(true);
                                Object id = beRelationField.get(d);
                                putCacheOperator.putCache(
                                        appNamePrefix
                                                + Constants.KEY_JOINER
                                                + context().keyPrefix()
                                                + Constants.KEY_JOINER
                                                + id,
                                        d);
                            } catch (NoSuchFieldException | IllegalAccessException e) {
                                e.printStackTrace();
                            }
                        }
                    });
            return ds;
        } else {
            return new ArrayList<>();
        }
    }

    @Override
    public Set<String> cacheKey() {
        Set<String> cacheKeySet = new HashSet<>();
        List<T> ids = ids();
        ids.forEach(
                id ->
                        cacheKeySet.add(
                                appNamePrefix
                                        + Constants.KEY_JOINER
                                        + context().keyPrefix()
                                        + Constants.KEY_JOINER
                                        + id));
        return cacheKeySet;
    }

    /**
     * 通过redis cacheKeyTo ids的转换
     *
     * @param cacheKeys : redis keys
     * @return : ids
     */
    @Override
    public List<T> cacheKeyToIds(List<String> cacheKeys) {
        List<String> ids =
                cacheKeys.stream()
                        .distinct()
                        .map(
                                redisKey ->
                                        redisKey.replace(
                                                appNamePrefix
                                                        + Constants.KEY_JOINER
                                                        + context().keyPrefix()
                                                        + Constants.KEY_JOINER,
                                                ""))
                        .collect(Collectors.toList());

        if (Long.class.equals(context().tClass())) {
            @SuppressWarnings("unchecked")
            List<T> transIds =
                    (List<T>) ids.stream().map(Long::valueOf).collect(Collectors.toList());
            return transIds;
        }
        if (String.class.equals(context().tClass())) {
            @SuppressWarnings("unchecked")
            List<T> transIds =
                    (List<T>) ids.stream().map(String::valueOf).collect(Collectors.toList());
            return transIds;
        }
        if (Integer.class.equals(context().tClass())) {
            @SuppressWarnings("unchecked")
            List<T> transIds =
                    (List<T>) ids.stream().map(Integer::valueOf).collect(Collectors.toList());
            return transIds;
        }
        return null;
    }

    protected CacheMappingContext context() {
        if (context == null) {
            Type type = this.getClass().getGenericSuperclass();
            ParameterizedType p = (ParameterizedType) type;
            Class<?> dClass = (Class<?>) p.getActualTypeArguments()[1];
            Class<?> tClass = (Class<?>) p.getActualTypeArguments()[2];
            String keyPrefix = dClass.getSimpleName();
            context =
                    new CacheMappingContext() {
                        @Override
                        public Class<?> dClass() {
                            return dClass;
                        }

                        @Override
                        public Class<?> tClass() {
                            return tClass;
                        }

                        @Override
                        public String keyPrefix() {
                            return keyPrefix;
                        }
                    };
        }
        return context;
    }

    /** context */
    interface CacheMappingContext {
        /**
         * 目标数据类型
         *
         * @return : dClass
         */
        Class<?> dClass();

        /**
         * id 类型
         *
         * @return : tClass
         */
        Class<?> tClass();

        /**
         * 构造的 redisKeyPrefix
         *
         * @return : redisKeyPrefix
         */
        String keyPrefix();
    }
}
